;;===============================================================
;; Aho-corasick unit tests
;;===============================================================
(ns strclj.ahocorasick-test
  (:use [strclj.ahocorasick :as src]
        [strclj.trie :as t]
        [clojure.test]
        [objects.node]
        [strclj.core-test])
  (:import [objects Node])
)

(deftest test-set-node-pointer
  (testing "set-node-pointer"
	  (let [pointing (node-make (array-map))
         pointed (node-make)]
     ; Set pointer
      (@#'src/set-node-pointer! pointing pointed 1 :k)
     ; Check nonexistant key
      (is (= nil (get (node-get-dict pointing) :stam)))
     ; Check :k
      (let [ptr (get (node-get-dict pointing) :k)
            ptr-node (get-node ptr)
            ptr-length (prefix-length ptr)]
        (is (= ptr-node pointed))
        (is (= 1 ptr-length))
      )
    )
  )
)

(deftest test-generate-sequence
  (testing "generate-sequence"
	  (let [node1 (node-make)
         node2 (node-make)
         node3 (node-make)
         node4 (node-make)]
     ; Set pointer
      (@#'src/set-fail-pointer! node1 node2 12)
      (@#'src/set-fail-pointer! node2 node3 23)
      (@#'src/set-fail-pointer! node3 node4 34)
      (let [from_one (@#'src/generate-sequence @#'src/get-fail-pointer node1)
            from_three (@#'src/generate-sequence @#'src/get-fail-pointer node3)]
        ; First should be node2 with length 12
        (is (= node2 (@#'get-node (first from_one))))
        (is (= 12 (prefix-length (first from_one))))
        ; Last is node4 with length 34
        (is (= node4 (@#'get-node (last from_one))))
        (is (= 34 (prefix-length (last from_one))))
        ; from_three should contain only node4, with length 34.
        (is (= node4 (@#'get-node (first from_three))))
        (is (= 34 (prefix-length (last from_three))))
        ; Test lengths.
        (is (= 3 (count from_one)))
        (is (= 1 (count from_three)))
      )
    )
  )
)

(deftest test-get-longest-suffix
  (testing "get-longest-suffix"
	  (let [trie (@#'t/trie-make ["abcd" "kmabco"])
          km_node (@#'t/trie-get trie "km")
          kma_node (@#'t/trie-get trie "kma")
          kmab_node (@#'t/trie-get trie "kmab")
          a_node (@#'t/trie-get trie "a")
          ab_node (@#'t/trie-get trie "ab")
          ]
      (@#'src/set-fail-pointer! kma_node a_node 1)
      (@#'src/set-fail-pointer! kmab_node ab_node 2)
      (is (= a_node (get-node (@#'src/get-longest-suffix trie kma_node \b))))
      (is (= ab_node (get-node (@#'src/get-longest-suffix trie kmab_node \c))))
    )
  )
)


(deftest test-upgrade-trie
  (testing "get-longest-suffix"
	  (let [trie (trie-make ["abcd" "kmabco" "ma" "mma" "km" "m"])]
      ; Upgrade trie to automaton
      (upgrade-trie! trie)
      ; Test fail pointers
      (are [word fail-word]
             (let [fail-ptr (@#'src/get-fail-pointer (t/trie-get trie word))]
               (if fail-ptr
                 (= (@#'src/get-node fail-ptr) (trie-get trie fail-word)) 
                 (= fail-ptr fail-word))
             )
             "km" "m" 
             "kma" "ma"
             "kmab" "ab"
             "kmabc" "abc"
             "kmabco" nil
             "ma" "a"
             "m" nil
      )
      ; Test output pointers
      (are [word output-word]
             (let [output-ptr (@#'src/get-output-pointer (t/trie-get trie word))]
               (if output-ptr
                 (= (@#'src/get-node output-ptr) (trie-get trie output-word)) 
                 (= output-ptr output-word))
             )
             "kma" "ma"
             "km" "m"
             "mma" "ma"
             "abcd" nil
             "a" nil
             
      )
    )
  )
)


(deftest test-ac-make
  (testing "ac-make"
	  (let [target-strings ["abcd" "kmabco" "kmgab"]
          trie (trie-make target-strings)
          automaton (ac-make target-strings)]
     (upgrade-trie! trie)
     ; Test equality of nodes for each prefix.
     (doseq [target-string target-strings]
	     (loop [word target-string] 
         (when (seq word) 
	         (let [trie-word (trie-get trie word)
	               automaton-word (trie-get automaton word)]
	           (is (= trie-word automaton-word)))
	         (recur (clojure.string/join (butlast word)))
         )
	     )
     )
     ; Test equality of max-length.
     (is (= (:max-length (node-get-dict trie)) (:max-length (node-get-dict automaton)))) 
    )
  )
)


(deftest test-ac-match
  (testing "ac-match"
	  (let [target-strings ["abcd" "kmabco" "kmgab" "aa" "aaaa" "jrkmsaksmd"]
          automaton (ac-make target-strings)
          results (ac-match automaton "This is abcd test string used tokmga and kmabc the zzzabckmabcor.")]
      (is (= (count results) 2))
      ; Checking abcd
      (is 
        (and 
          (= 8 (first (first results)))
          (= 11 (last (first results)))
        )
      )
      ; Checking kmabco
      (is 
        (and 
          (= 57 (first (last results)))
          (= 62 (last (second results)))
        )
      )
    )
  )
)


(deftest test-ac-matcher
  (testing "ac-matcher"
	  (let [target-strings ["abcd" "kmabco" "kmgab" "aa" "aaaa" "jrkmsaksmd"]
          automaton (ac-make target-strings)
          text "This is abcd test string used tokmga and kmabc the zzzabckmabcor."
          results (ac-match automaton text)
          matcher-results ((ac-matcher automaton) text)]
      (is (= results matcher-results))
    )
  )
)


(deftest test-ac
  (testing "ac"
	  (let [target-strings ["abcd" "kmabco" "kmgab" "aa" "aaaa" "jrkmsaksmd"]
          automaton (ac-make target-strings)
          text "This is abcd test string used tokmga and kmabc the zzzabckmabcor."
          results (ac-match automaton text)
          standalone-matcher (ac target-strings)]
      (is (= results (standalone-matcher text)))
    )
  )
)

